package com.kb.service.impl.gc;


import cn.iocoder.yudao.module.system.api.category.CategoryApi;
import cn.iocoder.yudao.module.system.api.category.dto.CategoryRespDTO;
import cn.iocoder.yudao.module.system.api.organization.OrganizationApi;
import cn.iocoder.yudao.module.system.api.organization.dto.OrganizationRespDTO;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.kb.common.ComponentIndex;
import com.kb.common.MeException;
import com.kb.domain.SplitFieldConfigDO;
import com.kb.dto.ComponentSyncDTO;
import com.kb.entity.*;
import com.kb.mapper.es.ComponentHistoryDao;
import com.kb.mapper.es.ComponentIndexDao;
import com.kb.mapper.es.SplitFieldConfigMapper;
import com.kb.service.gc.ComponentEsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author 国创工软
 */

@Service
@Slf4j
@DS("master")
public class ComponentEsServiceImpl implements ComponentEsService {


    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Resource
    private ComponentIndexDao componentIndexDao;

    @Resource
    private ComponentHistoryDao componentHistoryDao;

    @Resource
    private OrganizationApi organizationApi;

    @Resource
    private SplitFieldConfigMapper splitFieldConfigMapper;

    @Resource
    private CategoryApi categoryApi;


    @Override
    public Boolean saveComponentIndexData(ComponentSyncDTO syncDTO) {
        try {
            //变更删除原来的，再保存新的
            if (syncDTO.getTaskType() == 2) {
                List<ComponentData> componentDataList = syncDTO.getComponentDataList();
                List<Long> originalIds = componentDataList.stream().map(ComponentData::getOriginalId).collect(Collectors.toList());
                BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
                boolQueryBuilder.must(QueryBuilders.termsQuery("original_id", originalIds));
                NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()
                        .withQuery(boolQueryBuilder);
                NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build();
                try {
                    elasticsearchRestTemplate.delete(searchQuery, ComponentIndex.class);
                } catch (Exception e) {
                    throw new MeException(e.getMessage());
                }
                log.info("元器件产品变更删除原来产品{}成功", JSONObject.toJSONString(originalIds));
                List<ComponentIndex> componentIndexList = convertEsComponent(syncDTO);
                componentIndexDao.saveAll(componentIndexList);
                log.info("变更同步数据到ES成功");
                componentIndexList.clear();
            } else {
                List<ComponentIndex> componentIndexList = convertEsComponent(syncDTO);
                componentIndexDao.saveAll(componentIndexList);
                // 联想词写入
                List<String> words = Lists.newArrayList();
                for (ComponentIndex componentIndex : componentIndexList) {
                    words.add(componentIndex.getPartNumber());
                    words.addAll(Arrays.asList(componentIndex.getCategoryName().split("/")));
                    words.add(componentIndex.getOrgName());
                }
                componentHistoryDao.insertSearchHistory(words);
                log.info("元器件产品同步数据到ES成功");
                words.clear();
                componentIndexList.clear();
            }
            return Boolean.TRUE;
        } catch (Exception e) {
            log.error("元器件产品布同步数据到ES失败", e);
            return Boolean.FALSE;
        }
    }


    private List<ComponentIndex> convertEsComponent(ComponentSyncDTO syncDTO) {
        List<ComponentIndex> componentIndexList = new ArrayList<>();
        List<ComponentData> componentDataList = syncDTO.getComponentDataList();
        Map<Long, List<Header>> headMap = syncDTO.getComponentDataHeaderList().stream().
                collect(Collectors.groupingBy(Header::getId));
        List<SplitFieldConfigDO> splitFieldConfigList = splitFieldConfigMapper.selectList(null);
        Set<Long> orgIds = componentDataList.stream().map(ComponentData::getOrgId).collect(Collectors.toSet());

        List<OrganizationRespDTO> orgList = organizationApi.getOrganizations(orgIds).getCheckedData();
        Map<Long, List<OrganizationRespDTO>> orgIdMap = orgList.stream().collect(Collectors.groupingBy(OrganizationRespDTO::getId));
        for (ComponentData componentDataDTO : componentDataList) {
            ComponentIndex componentIndex = new ComponentIndex();
            componentIndex.setId(String.valueOf(componentDataDTO.getId()));
            componentIndex.setPartNumber(componentDataDTO.getPartNumber());
            componentIndex.setImageIds(componentDataDTO.getComponentImages());
            componentIndex.setOriginalId(componentDataDTO.getOriginalId());
            componentIndex.setDatasheetName(componentDataDTO.getDatasheetName());
            componentIndex.setDatasheetFileId(componentDataDTO.getDatasheetFileId());
            componentIndex.setSymbolId(handleSb(syncDTO, componentDataDTO));
            componentIndex.setFootprintId(handleFp(syncDTO, componentDataDTO));
            componentIndex.setModel3dId(handleModel3d(syncDTO, componentDataDTO));
            componentIndex.setRoHs(handleIsEnvProtect("RoHS", syncDTO, componentDataDTO));
            componentIndex.setReach(handleIsEnvProtect("REACH", syncDTO, componentDataDTO));
            componentIndex.setHf(handleIsEnvProtect("HF", syncDTO, componentDataDTO));
            componentIndex.setOrgId(String.valueOf(componentDataDTO.getOrgId()));
            String organizationName = orgIdMap.get(componentDataDTO.getOrgId()).get(0).getOrganizationName();
            componentIndex.setOrgName(organizationName);
            Header componentDataHeader = headMap.get(componentDataDTO.getSetId()).get(0);
            componentIndex.setPlatCatId(String.valueOf(componentDataHeader.getPlatformCategoryId()));
            componentIndex.setOrgCatId(String.valueOf(componentDataHeader.getOrgCategoryId()));
            componentIndex.setCategoryName(getCategoriesName(componentDataHeader.getPlatformCategoryId()));
            componentIndex.setDescription(componentDataDTO.getDescription());
            componentIndex.setProductParams(generateProductParams(syncDTO, componentDataDTO));
            componentIndex.setSimilarityAndAggParams(assembleAggregationField(syncDTO, componentDataDTO, splitFieldConfigList));
            componentIndex.setSearchParams(handleSearchParams(componentDataDTO, componentIndex));
            componentIndexList.add(componentIndex);
        }
        return componentIndexList;
    }

    private String generateProductParams(ComponentSyncDTO syncDTO, ComponentData componentDataDTO) {
        Long setId = componentDataDTO.getSetId();
        List<ComponentDataHeaderField> fieldList = syncDTO.getComponentDataHeaderFieldList().stream().
                filter(headerField -> headerField.getSetId().equals(setId)).collect(Collectors.toList());
        //字段值map
        Map keyFieldMap = JSONObject.parseObject(componentDataDTO.getTechTemplateFields(), Map.class);
        JSONObject paramsObj = new JSONObject();
        for (ComponentDataHeaderField headerField : fieldList) {
            String fieldName = headerField.getFieldName();
            String fieldValue = keyFieldMap.get(headerField.getFieldKey()).toString();
            paramsObj.put(fieldName, fieldValue);
        }
        return paramsObj.toJSONString();
    }

    private String handleSearchParams(ComponentData componentDataDTO, ComponentIndex componentIndex) {
        Map<String, Object> similarityAndAggParams = componentIndex.getSimilarityAndAggParams();
        String similarityAndAggParamsStr = JSONObject.toJSONString(similarityAndAggParams);
        String s = similarityAndAggParamsStr.replace("{", "").replace("}", "");
        String description = componentDataDTO.getDescription();
        StringBuilder stringBuilder = new StringBuilder(s);
        if (StringUtils.isNotEmpty(description)) {
            stringBuilder.append(",").append(description);
        }
        return stringBuilder.toString();
    }

    private Map<String, Object> assembleAggregationField(ComponentSyncDTO syncDTO, ComponentData componentDataDTO,
                                                         List<SplitFieldConfigDO> splitFielList) {
        Map<String, Object> similarityFieldMap = new HashMap<>();
        List<String> splitFieldNameList = splitFielList.stream().map(SplitFieldConfigDO::getFieldName).collect(Collectors.toList());
        Long setId = componentDataDTO.getSetId();
        List<ComponentDataHeaderField> fieldList = syncDTO.getComponentDataHeaderFieldList().stream().
                filter(headerField -> headerField.getSetId().equals(setId)).collect(Collectors.toList());
        //字段值map
        Map keyFieldMap = JSONObject.parseObject(componentDataDTO.getTechTemplateFields(), Map.class);
        for (ComponentDataHeaderField headField : fieldList) {
            String fieldNameNew = handleFieldName(headField.getFieldName());
            String fieldValue = keyFieldMap.get(headField.getFieldKey()).toString();
            String fieldValueNew = fieldValue.replaceAll(" ", "").replaceAll(",", "");
            if (StringUtils.isEmpty(fieldValueNew) || "-".equals(fieldValueNew) || "/".equals(fieldValueNew)) {
                continue;
            }
            //需要拆的字段
            if (CollectionUtils.isNotEmpty(splitFieldNameList) && splitFieldNameList.contains(fieldNameNew)) {
                splitFieldByValue(fieldNameNew, fieldValueNew, similarityFieldMap);
            } else if (isNeedHandleNumValue(fieldValueNew)) {
                //是否需要数值处理
                String keyDouble = String.format("%s_double", fieldNameNew);
                similarityFieldMap.put(keyDouble, handleNumberValue(fieldValueNew));
                similarityFieldMap.put(fieldNameNew, fieldValueNew);
            } else {
                similarityFieldMap.put(fieldNameNew, fieldValueNew);
            }
        }
        return similarityFieldMap;
    }

    private void splitFieldByValue(String fieldName, String fieldValue, Map<String, Object> similarityFieldMap) {
        if (fieldValue.contains("~")) {
            String[] split = fieldValue.split("~");
            String minAggKey = String.format("最小%s", fieldName);
            String minSimiKey = String.format("最小%s_double", fieldName);
            similarityFieldMap.put(minAggKey, split[0].trim());
            similarityFieldMap.put(minSimiKey, handleNumberValue(split[0]));
            String maxAggKey = String.format("最大%s", fieldName);
            String maxSimiKey = String.format("最大%s_double", fieldName);
            similarityFieldMap.put(maxAggKey, split[1].trim());
            similarityFieldMap.put(maxSimiKey, handleNumberValue(split[1]));
        } else if (fieldValue.contains("±")) {
            String replace = fieldValue.replace("±", "");
            String leftValue = "-" + replace;
            String right = "+" + replace;
            String minAggKey = String.format("最小%s", fieldName);
            String minSimiKey = String.format("最小%s_double", fieldName);
            similarityFieldMap.put(minAggKey, leftValue.trim());
            similarityFieldMap.put(minSimiKey, handleNumberValue(leftValue));
            String maxAggKey = String.format("最大%s", fieldName);
            String maxSimiKey = String.format("最大%s_double", fieldName);
            similarityFieldMap.put(maxAggKey, right.trim());
            similarityFieldMap.put(maxSimiKey, handleNumberValue(right));
        } else if (fieldValue.contains(",") || fieldValue.contains("，")) {
            String key = String.format("%s", fieldName);
            String[] split;
            if (fieldValue.contains(",")) {
                split = fieldValue.split(",");
                similarityFieldMap.put(key, split);
            }
            if (fieldValue.contains("，")) {
                split = fieldValue.split("，");
                similarityFieldMap.put(key, split);
            }
        }
    }


    private Long handleModel3d(ComponentSyncDTO syncDTO, ComponentData componentDataDTO) {
        Map<Long, List<ComponentModel3d>> model3dMap = syncDTO.getComponentModel3dList().stream().collect(Collectors.
                groupingBy(ComponentModel3d::getId));
        List<ComponentModel3d> componentModel3dList = model3dMap.get(componentDataDTO.getModel3dId());
        if (CollectionUtils.isNotEmpty(componentModel3dList)) {
            return componentModel3dList.get(0).getStep();
        }
        return null;
    }


    private Long handleFp(ComponentSyncDTO syncDTO, ComponentData componentDataDTO) {
        Map<Long, List<ComponentModelFP>> fpIdMap = syncDTO.getComponentModelFpList().stream().collect(Collectors.
                groupingBy(ComponentModelFP::getId));
        List<ComponentModelFP> componentModelFpList = fpIdMap.get(componentDataDTO.getFootprintId());
        if (CollectionUtils.isNotEmpty(componentModelFpList)) {
            return componentModelFpList.get(0).getVxinFootprint();
        }
        return null;
    }

    private Long handleSb(ComponentSyncDTO syncDTO, ComponentData componentDataDTO) {
        Map<Long, List<ComponentModelSb>> sbIdMap = syncDTO.getComponentModelSbList().stream().collect(Collectors.
                groupingBy(ComponentModelSb::getId));
        List<ComponentModelSb> componentModelSbList = sbIdMap.get(componentDataDTO.getSymbolId());
        if (CollectionUtils.isNotEmpty(componentModelSbList)) {
            return componentModelSbList.get(0).getVxinSymbol();
        }
        return null;
    }


    private Boolean handleIsEnvProtect(String name, ComponentSyncDTO syncDTO, ComponentData componentDataDTO) {
        Long setId = componentDataDTO.getSetId();
        List<ComponentDataHeaderField> fieldList = syncDTO.getComponentDataHeaderFieldList().stream().
                filter(headerField -> headerField.getSetId().equals(setId)).collect(Collectors.toList());
        Map<String, List<ComponentDataHeaderField>> fieldNameMap = fieldList.stream().collect(Collectors.
                groupingBy(ComponentDataHeaderField::getFieldName));
        //可不存在
        List<ComponentDataHeaderField> headerFieldList = fieldNameMap.get(name);
        if (CollectionUtils.isNotEmpty(headerFieldList)) {
            ComponentDataHeaderField componentDataHeaderFieldDTO = headerFieldList.get(0);
            Map keyFieldMap = JSONObject.parseObject(componentDataDTO.getTechTemplateFields(), Map.class);
            String value = keyFieldMap.get(componentDataHeaderFieldDTO.getFieldKey()).toString();
            return "符合".equals(value);
        } else {
            return Boolean.FALSE;
        }
    }

    private double handleNumberValue(String fieldValue) {
        String regex = "-?\\+?\\d+\\.?\\d*";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(fieldValue);
        BigDecimal number = new BigDecimal("0");
        while (matcher.find()) {
            number = new BigDecimal(matcher.group());
        }
        return number.setScale(4, RoundingMode.HALF_UP).doubleValue();
    }

    private boolean isNeedHandleNumValue(String fieldValue) {
        String regexWithUnit = "(\\d+(\\.\\d+)?)\\s*(µH|Ω|mΩ|KΩ|MΩ|GΩ|TΩ|µΩ|µF|µA|µV|µm|µs|µg|m/s|km/h|m/s2|kW·h)";
        Pattern patternUnit = Pattern.compile(regexWithUnit);
        Matcher matcherUnit = patternUnit.matcher(fieldValue);
        if (!matcherUnit.matches()) {
            String regex;
            if (isNumeric(fieldValue)) {
                regex = "-?\\+?\\d+\\.?\\d*";
            } else {
                regex = "-?\\+?\\d+\\.?\\d*\\w+";
            }
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(fieldValue);
            return matcher.matches();
        } else {
            return matcherUnit.matches();
        }
    }

    public static boolean isNumeric(String str) {
        if (Objects.isNull(str)) {
            return false;
        }
        for (char c : str.toCharArray()) {
            if (!Character.isDigit(c)) {
                return false;
            }
        }
        return true;
    }

    private String handleFieldName(String fieldName) {
        if (StringUtils.isNotEmpty(fieldName)) {
            return fieldName.replaceAll("\\s*", "").replaceAll("\\n< >", "")
                    .replaceAll("<>", "").replaceAll("< >", "")
                    .replace("*", "").replaceAll(" ", "").replaceAll("\\u00A0", "");
        } else {
            return fieldName;
        }
    }

    private String getCategoriesName(Long platformCategoryId) {
        CategoryRespDTO firstCategory;
        CategoryRespDTO secondCategory;
        CategoryRespDTO thirdCategory = categoryApi.getById(platformCategoryId).getData();
        secondCategory = categoryApi.getById(thirdCategory.getParentId()).getData();
        String splitStr = "/";
        if (Objects.isNull(secondCategory) || Objects.isNull(secondCategory.getId())) {
            return thirdCategory.getName();
        } else {
            //获取一级分类
            firstCategory = categoryApi.getById(secondCategory.getParentId()).getData();
            StringBuilder stringBuilder = new StringBuilder();
            if (Objects.nonNull(firstCategory) && Objects.isNull(firstCategory.getId())) {
                stringBuilder.append(secondCategory.getName()).append(splitStr).append(thirdCategory.getName());
                return stringBuilder.toString();
            } else {
                stringBuilder.append(firstCategory.getName()).append(splitStr);
                stringBuilder.append(secondCategory.getName()).append(splitStr).append(thirdCategory.getName());
                return stringBuilder.toString();
            }
        }
    }
}
